/*
@authors - Siddharth Sathyam, Pat LaMontagna, Alan Ruvalcaba
@Description - Server Source Code

10/13/2011 ssathy2 Written
10/15/2011 ssathy2 Modified. Added Comments
**/

#include "server.h"

Server::Server(QObject * parent) :
    QTcpServer(parent)
{

}

//
//Method Description:
//- void startServer(QHostAddress addr, quint16 port);
//- Starts the server with the given host address and port. Returns if successful or not

bool Server::startServer(QHostAddress addr, quint16 port)
{
    return this->listen(addr, port);
}

//
//Method Description:
//- void incomingConnection(int handle);
//- Function handles incoming connections from users to the server.

void Server::incomingConnection(int handle) {

    //create a socket with this handle to talk to the client
    QTcpSocket *client = new QTcpSocket(this);
    client->setSocketDescriptor(handle);

    qDebug() << QTime::currentTime().toString(Qt::SystemLocaleDate) << ": Client Connected From - " << client->peerAddress().toString() << ":" << client->peerPort();
    connect(client, SIGNAL(readyRead()), this, SLOT(handleIncomingData()));
    connect(client, SIGNAL(disconnected()), this, SLOT(handleDisconnection()));
}

//
//Method Description:
//- void handleIncomingData();
//- Function that handles any incoming data from any user and deals with it in the appropriate manner

void Server::handleIncomingData() {

    // create a pointer to the socket that sent data
    QTcpSocket *socketThatSentData = (QTcpSocket*)sender();

    QDataStream recvStream(socketThatSentData);
    recvStream.setVersion(QDataStream::Qt_4_0);
    quint16 packId;
    quint16 blockSize = 0;
    // read in data from the socket in accordance with the protocol that was decided

    if(blockSize == 0)
    {
        if(socketThatSentData->bytesAvailable() < sizeof(quint16))
        {
            return;
        }
        recvStream >> packId;
        // if the user sends an establishing data packet, then deal with that separately
        if(packId == 0)
        {
            QString userFromSocket;

            blockSize = sizeof(QString);
            if(socketThatSentData->bytesAvailable() < blockSize)
            {
                return;
            }

            recvStream >> userFromSocket;

	    qDebug() << QTime::currentTime().toString(Qt::SystemLocaleDate) << ": " << "Client: " << userFromSocket << " at " << socketThatSentData->peerAddress().toString() << "@" << socketThatSentData->peerPort() << endl << flush;

	    if(!reverseLookupUserSocketStringTable.contains(userFromSocket))
	    {
		userSocketStringTable.insert(socketThatSentData, userFromSocket);
		reverseLookupUserSocketStringTable.insert(userFromSocket, socketThatSentData);
	    }
	    else
	    {
		//@TODO The username that the user sent already sent so tell the user to provide another username
	    }

	    qDebug() << "Users Online: " << userSocketStringTable.count() << endl;

	    // Send the user a list of all clients connected
            sendUsersOnline();
        }
        // User sent a message so route it to the right person
        else if(packId == 1)
        {
            QString messageSentFromUser;
            QString messageSentToUser;
            QString message;
            QString timeSent;

            blockSize = sizeof(QString) + sizeof(QString)+ sizeof(QString) + sizeof(QString);
            if (socketThatSentData->bytesAvailable() <  blockSize)
            {
                return;
            }
            recvStream >> messageSentFromUser >> messageSentToUser >> message >> timeSent;
	    qDebug() << timeSent << ": " << messageSentFromUser << " to " << messageSentToUser << "- " << message << endl << flush;

            // Pass on the message
            if(!sendMessage(messageSentFromUser, messageSentToUser, message, timeSent))
            {
		qDebug() << QTime::currentTime().toString(Qt::SystemLocaleDate) << ": " << "Message from " << messageSentFromUser << " at " << timeSent << " Was Not Successfully sent" << endl << flush;
            }
        }
        else
        {
            qDebug() << "Invalid request";
        }
    }
}

//
//Method Description:
//- void handleDisconnection();
//- Deals with users disconnecting from the server

void Server::handleDisconnection() {

    // Create a socket that will be a pointer to the socket that sent the signal
    QTcpSocket *disconnectingClient = (QTcpSocket*)sender();

    if(userSocketStringTable.contains(disconnectingClient))
    {
        QString disconnectingUsername = userSocketStringTable.find(disconnectingClient).value();

        //remove socket and username from the two tables
        reverseLookupUserSocketStringTable.remove(disconnectingUsername);
        userSocketStringTable.remove(disconnectingClient);

        // Send the Updated user list to the clients here
        sendUsersOnline();

	qDebug() << QTime::currentTime().toString(Qt::SystemLocaleDate) <<  ": Client disconnected - " << disconnectingUsername << "@" << disconnectingClient->peerAddress().toString() << ":" << disconnectingClient->peerPort();
	qDebug() << "Users Online: " << userSocketStringTable.count() << endl;
    }
    else
    {
	//@TODO Client Disconnection Problem...Uh oh something went horribly wrong
    }
}

//
//Method Description:
//- void sendUsersOnline();
//- Sends a QStringList of the users that are online to all of the users that are currently online

void Server::sendUsersOnline() {

    QStringList listOfUsers;
    QByteArray dataBlock;
    QDataStream sendStream(&dataBlock, QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_0);

    // save the list of users to a stringList
    foreach (QString name, userSocketStringTable.values())
    {
        listOfUsers << name;
    }

    // Users Protocol: (2) - (QStringList of usersOnline)
    sendStream << quint16(2) << listOfUsers;

    // send the list to all users that are connected to the server with the above protocol
    foreach(QTcpSocket* sock, userSocketStringTable.keys())
    {
        sock->write(dataBlock);
    }
}

//
//Method Description:
//-bool sendMessage(QString msgSentFrom, QString msgSentTo, QString message, QString timeSent);
//-Returns a bool variable specifing if the message was successfully sent.
//-Takes a QString with the user that sent the message, QString with the user that the message should go to,
// a QString with the message, and a QString with the time at which the message was sent at. Sends a message,
// with the protocol header to intended message recipient.

bool Server::sendMessage(QString msgSentFrom, QString msgSentTo, QString message, QString timeSent) {

    if(!reverseLookupUserSocketStringTable.contains(msgSentTo))
    {
        return false;
    }
    else
    {
        QTcpSocket *toUser = reverseLookupUserSocketStringTable.find(msgSentTo).value();
        QByteArray datablock;
        QDataStream sendStream(&datablock , QIODevice::WriteOnly);
        sendStream.setVersion(QDataStream::Qt_4_0);
        sendStream << quint16(1) << (QString) msgSentFrom << (QString) msgSentTo << (QString) message << (QString) timeSent;
        toUser->write(datablock);
        return true;
    }
}

